package main

import (
	"context"
	"github.com/frankrap/huobi-api/hbdm"
	"log"
	"math"
	"sync"
	"time"
)

var profit = 4

type AverageKline struct {
	FiveAverage   float64 // 5 个15分钟均线值
	TenAverage    float64 // 10 个15分钟均线值
	ThirtyAverage float64 // 30 个15分钟均线值
}

type Trade struct {
	Average            *AverageKline
	client             *hbdm.Client
	BuyRatio           float64
	SellRatio          float64
	RecommendBuyPrice  float64
	RecommendSellPrice float64
	RecommendDirection int              // 0 买 1卖
	Account            hbdm.AccountInfo // 0 买 1卖
	BuyPosition        hbdm.Position    //
	SellPosition       hbdm.Position    //
	UnSellCount        uint             //
	UnBuyCount         uint             //
	conf               *Config          //
	forceSell          int              // 强势下跌信号 防止山顶
	forceBuy           int              // 强势底部回调信号 防止踏空
	optLock            sync.Mutex
	MaxAverage         float64
	MinAverage         float64
}

func NewTrade(client *hbdm.Client, config *Config) *Trade {
	t := &Trade{}
	t.client = client
	t.conf = config
	t.Account = hbdm.AccountInfo{}
	t.BuyPosition = hbdm.Position{}
	t.SellPosition = hbdm.Position{}
	t.Average = &AverageKline{
		FiveAverage:   0,
		TenAverage:    0,
		ThirtyAverage: 0,
	}
	return t
}

// 计算15分钟 均线
func (this *Trade) CronCalcKline(wg *sync.WaitGroup, ctx context.Context, symbol string) {
	defer wg.Done()
	t := time.NewTicker(10 * time.Second)
	defer t.Stop()
	for {
		select {
		case <-ctx.Done():
			log.Println("退出计算15分钟均线")
			return
		case <-t.C:
			// 1 分钟
			klineData, err := this.client.GetCoinKLine(symbol, "15min", 30, int64(0), int64(0))
			if err != nil {
				log.Println("请求出错", err)
				continue
			}
			this.JqAverage(klineData.Data)
		}
	}
}

// 计算 持仓比
func (this *Trade) CronCalcRatio(wg *sync.WaitGroup, ctx context.Context, symbol string) {
	defer wg.Done()
	t := time.NewTicker(1 * time.Minute)
	defer t.Stop()
	for {
		select {
		case <-ctx.Done():
			log.Println("退出请求持仓比")
			return
		case <-t.C:
			// 1 分钟
			coinRatio, err := this.client.GetCoinRatio(symbol, "1day")
			if err != nil {
				log.Println("请求出错", err)
				continue
			}
			l := len(coinRatio.Data.List)
			this.BuyRatio = coinRatio.Data.List[l-1].BuyRatio
			this.SellRatio = coinRatio.Data.List[l-1].SellRatio
			log.Println("多仓的总持仓量占比", this.BuyRatio)
			log.Println("空仓的总持仓量占比", this.SellRatio)
		}
	}
}

// 计算 深度
func (this *Trade) CronCalcDept(wg *sync.WaitGroup, ctx context.Context, symbol string) {
	defer wg.Done()
	t := time.NewTicker(30 * time.Second)
	defer t.Stop()
	for {
		select {
		case <-ctx.Done():
			log.Println("退出请求深度")
			return
		case <-t.C:
			// 深度
			marketDept, err := this.client.GetCoinMarketDepth(symbol, "step9")
			if err != nil {
				log.Println("请求出错", err)
				continue
			}
			if len(marketDept.Tick.Asks) < 1 {
				log.Println("请求出错", marketDept.Tick.Asks)
				continue
			}
			this.RecommendSellPrice = marketDept.Tick.Asks[0][0]
			log.Println("当前卖一价 ", this.RecommendSellPrice, marketDept.Tick.Asks[0][1])
			this.RecommendBuyPrice = marketDept.Tick.Bids[0][0]
			log.Println("当前买一价 ", this.RecommendBuyPrice, marketDept.Tick.Bids[0][1])
			this.RecommendDirection = 0
			if marketDept.Tick.Asks[0][1] > marketDept.Tick.Bids[0][1] {
				this.RecommendDirection = 1
				log.Println("当前推荐卖", this.RecommendSellPrice)
			} else {
				log.Println("当前推荐买", this.RecommendBuyPrice)
			}
			if this.BuyRatio > 0 && this.SellRatio > 0 {
				if this.BuyRatio/this.SellRatio >= 2 {
					// 估计会有空的风险 爆多信号
					this.RecommendDirection = 1
				}
				if this.SellRatio/this.BuyRatio >= 2 {
					// 估计会有多的风险 爆空信号
					this.RecommendDirection = 0
				}
			}
		}
	}
}

// 监控账户盈利情况
func (this *Trade) CronAccount(wg *sync.WaitGroup, ctx context.Context, symbol string) {
	defer wg.Done()
	t := time.NewTicker(5 * time.Second)
	defer t.Stop()
	for {
		select {
		case <-ctx.Done():
			log.Println("退出请求账户信息")
			return
		case <-t.C:
			// 账户详情
			accountInfo, err := this.client.GetSwapAccountInfo(symbol)
			if err != nil {
				log.Println("请求出错", err)
				continue
			}
			for _, v := range accountInfo.Data {
				if v.ContractCode == symbol {
					this.Account = v
					break
				}
			}
			log.Println("账户权益", this.Account.ContractCode, this.Account.MarginAvailable, "未实现盈亏", this.Account.ProfitUnreal)
		}
	}
}

// 平单策略
func (this *Trade) CloseOrder(symbol string) {
	if this.BuyPosition.ProfitUnreal <= -float64(profit)/2 {
		// 闪电平仓
		_, err := this.client.SwapLightOrder(symbol,
			symbol, this.BuyPosition.Volume, "sell")
		if err != nil {
			log.Println("[闪电平仓][止损单]平多单", err)
			return
		}
		this.BuyPosition = hbdm.Position{}
	}
	if this.SellPosition.ProfitUnreal <= -float64(profit)/2 {
		// 闪电平仓
		_, err := this.client.SwapLightOrder(symbol,
			symbol, this.SellPosition.Volume, "buy")
		if err != nil {
			log.Println("[闪电平仓][止损单]平空单", err)
			return
		}
		this.SellPosition = hbdm.Position{}
	}
	// 如果账户盈利大于 10U 则出 考虑单边行情 一方亏损较多
	if this.Account.ProfitUnreal > 10 {
		if this.BuyPosition.Volume > 0 {
			// 闪电平仓
			_, err := this.client.SwapLightOrder(symbol,
				symbol, this.BuyPosition.Volume, "sell")
			if err != nil {
				log.Println("闪电平仓 平多单", err)
				return
			}
			this.BuyPosition = hbdm.Position{}
		}
		if this.SellPosition.Volume > 0 {
			// 闪电平仓
			_, err := this.client.SwapLightOrder(symbol,
				symbol, this.SellPosition.Volume, "buy")
			if err != nil {
				log.Println("闪电平仓 平空单", err)
				return
			}
			this.SellPosition = hbdm.Position{}
		}
	}
	log.Println("账户盈利情况", this.BuyPosition.ProfitUnreal, this.BuyPosition.Volume, this.SellPosition.ProfitRate)
	// 震动的方式盈利 另一方大于 -50% 的收益
	if this.BuyPosition.ProfitUnreal >= float64(profit) {
		// 闪电平仓
		res, err := this.client.SwapLightOrder(symbol,
			symbol, this.BuyPosition.Volume, "sell")
		if err != nil {
			log.Println("闪电平仓 平多单", err, res)
			return
		}
		log.Println("平多单成功", res)
		this.BuyPosition = hbdm.Position{}
	}
	if this.SellPosition.ProfitUnreal >= float64(profit) {
		// 闪电平仓
		res, err := this.client.SwapLightOrder(symbol,
			symbol, this.SellPosition.Volume, "buy")
		if err != nil {
			log.Println("闪电平仓 平空单", err, res)
			return
		}
		log.Println("平空单成功", res)
		this.SellPosition = hbdm.Position{}
	}
}

// 监控账户盈利情况
func (this *Trade) CronPosition(wg *sync.WaitGroup, ctx context.Context, symbol string) {
	defer wg.Done()
	t := time.NewTicker(2 * time.Second)
	defer t.Stop()
	for {
		select {
		case <-ctx.Done():
			log.Println("退出请求持仓信息")
			return
		case <-t.C:
			// 账户详情
			positionInfo, err := this.client.GetSwapPositionInfo(symbol)
			if err != nil {
				log.Println("请求出错", err)
				continue
			}
			this.BuyPosition = hbdm.Position{}
			this.SellPosition = hbdm.Position{}
			if len(positionInfo.Data) < 1 {
				log.Println("暂无持仓数据")
				continue
			}
			for _, v := range positionInfo.Data {
				if v.ContractCode != symbol {
					continue
				}
				if v.Direction == "buy" {
					this.BuyPosition = v
					log.Println("当前买单盈利 ", v.ContractCode, v.Profit, "数量", v.Volume, "开仓价", v.CostOpen)
				} else {
					this.SellPosition = v
					log.Println("当前空单盈利 ", v.ContractCode, v.Profit, "数量", v.Volume, "开仓价", v.CostOpen)
				}
			}
			this.CloseOrder(symbol)
		}
	}
}

func (this *Trade) HandleUnOrders(symbol string, orders []hbdm.Order) {
	this.optLock.Lock()
	defer this.optLock.Unlock()
	for _, v := range orders {
		if v.ContractCode != symbol {
			continue
		}
		if v.Direction == "buy" {
			this.UnBuyCount++
		} else {
			this.UnSellCount++
		}
		if v.Offset == "close" {
			// 止盈单
			continue
		}
		v.CreatedAt /= 1000
		log.Println("现在时间", time.Now().Unix(), "委托订单时间", v.CreatedAt)
		// 15分钟未成交 则进行取消订单
		// 平均线看的15分钟 挂15分钟单
		if time.Now().Unix()-v.CreatedAt >= 300*5 {
			or, err := this.client.SwapCancel(symbol, v.OrderID, 0)
			if err != nil {
				log.Println("取消订单", err)
				continue
			}
			if v.Direction == "buy" {
				this.UnBuyCount--
			} else {
				this.UnSellCount--
			}
			log.Println("取消订单", or.ErrMsg, or.ErrCode)
		}
	}
}

// 监控账户订单情况
func (this *Trade) CronOrders(wg *sync.WaitGroup, ctx context.Context, symbol string) {
	defer wg.Done()
	t := time.NewTicker(10 * time.Second)
	defer t.Stop()
	for {
		select {
		case <-ctx.Done():
			log.Println("退出请求委托列表")
			return
		case <-t.C:
			// 账户详情
			orders, err := this.client.GetSwapOpenOrders(symbol, 1, 50)
			if err != nil {
				log.Println("订单请求出错", err)
				continue
			}
			if len(orders.Data.Orders) < 1 {
				this.UnBuyCount = 0
				this.UnSellCount = 0
				log.Println("暂无委托订单数据")
				continue
			}
			this.HandleUnOrders(symbol, orders.Data.Orders)
		}
	}
}

// 下单
func (this *Trade) CreateOrders(wg *sync.WaitGroup, ctx context.Context, symbol string) {
	defer wg.Done()
	t := time.NewTicker(5 * time.Second)
	defer t.Stop()
	for {
		select {
		case <-ctx.Done():
			log.Println("退出创建订单")
			return
		case <-t.C:
			if this.Average.FiveAverage <= 0 || this.Average.TenAverage <= 0 || this.Average.ThirtyAverage <= 0 {
				log.Println("尚未初始化")
				continue
			}
			if this.BuyPosition.Volume > 0 && this.SellPosition.Volume > 0 {
				if this.BuyPosition.CostOpen < this.SellPosition.CostOpen {
					log.Println("已有两种持仓")
					continue
				}
			}
			this.COrder(symbol)
		}
	}
}

func (this *Trade) COrder(symbol string) {
	this.optLock.Lock()
	defer this.optLock.Unlock()
	orderPriceType := "limit"
	direction := "buy"
	offset := "open"
	volume := this.conf.Volume // 10张
	lever_rate := 50           // 50倍
	if this.BuyPosition.ProfitRate < 0 {
		volume = this.BuyPosition.Volume
		volume += float64(int64(this.BuyPosition.ProfitRate * 10))
	}
	if this.SellPosition.ProfitRate < 0 {
		volume = this.SellPosition.Volume
		volume += float64(int64(this.SellPosition.ProfitRate * 10))
	}
	clientOrderID := time.Now().UnixNano()
	// 如果山顶信号 空
	if this.forceSell == 1 {
		if this.UnSellCount > 0 {
			log.Println("委托买单数", this.UnBuyCount)
			// 有正在挂的单
			return
		}
		if this.SellPosition.Volume/this.conf.Volume >= 2 {
			return
		}
		log.Println("【山顶信号 下跌】**********进行单边策略1分析", this.RecommendDirection, this.RecommendBuyPrice, this.RecommendSellPrice)
		orderPriceType = "opponent"
		// 可以做空
		_, err := this.client.SwapOrder(symbol, symbol, orderPriceType, clientOrderID,
			this.RecommendBuyPrice, volume, "sell", offset, lever_rate)
		if err != nil {
			log.Println("下单失败", err)
			return
		}
		this.forceSell = 0
		return
	}
	// 如果山底信号 多
	if this.forceBuy == 1 {
		if this.UnBuyCount > 0 {
			log.Println("委托买单数", this.UnBuyCount)
			// 有正在挂的单
			return
		}
		if this.BuyPosition.Volume/this.conf.Volume >= 2 {
			return
		}
		log.Println("【山底信号 上涨】**********进行单边策略1分析", this.RecommendDirection, this.RecommendBuyPrice, this.RecommendSellPrice)
		orderPriceType = "opponent"
		// 可以做空
		_, err := this.client.SwapOrder(symbol, symbol, orderPriceType, clientOrderID,
			this.RecommendBuyPrice, volume, "buy", offset, lever_rate)
		if err != nil {
			log.Println("下单失败", err)
			return
		}
		this.forceBuy = 0
		return
	}

	// 如果 损失10 U 就反向做单
	if this.BuyPosition.ProfitUnreal < -10 {
		if this.UnSellCount > 0 {
			log.Println("委托买单数", this.UnBuyCount)
			// 有正在挂的单
			return
		}
		if this.SellPosition.Volume/this.conf.Volume >= 2 {
			return
		}
		log.Println("【单边行情 下跌】**********进行单边策略1分析", this.RecommendDirection, this.RecommendBuyPrice, this.RecommendSellPrice)
		orderPriceType = "opponent"
		// 可以做空
		_, err := this.client.SwapOrder(symbol, symbol, orderPriceType, clientOrderID,
			this.RecommendBuyPrice, volume, "sell", offset, lever_rate)
		if err != nil {
			log.Println("下单失败", err)
			return
		}
		return

	}
	// 如果 损失10 U 就反向做单
	if this.SellPosition.ProfitUnreal < -10 {
		if this.UnBuyCount > 0 {
			log.Println("委托买单数", this.UnBuyCount)
			// 有正在挂的单
			return
		}
		if this.BuyPosition.Volume/this.conf.Volume >= 2 {
			return
		}
		log.Println("【单边行情 上涨】**********进行单边策略2分析", this.RecommendDirection, this.RecommendBuyPrice, this.RecommendSellPrice)
		orderPriceType = "opponent"
		// 可以做多
		_, err := this.client.SwapOrder(symbol, symbol, orderPriceType, clientOrderID,
			this.RecommendBuyPrice, volume, "buy", offset, lever_rate)
		if err != nil {
			log.Println("下单失败", err)
			return
		}
		return
	}
	if this.Average.FiveAverage > this.Average.TenAverage &&
		this.Average.TenAverage > this.Average.ThirtyAverage {
		log.Println("**********进行策略1分析", this.RecommendDirection, this.RecommendBuyPrice, this.RecommendSellPrice)
		if math.Abs(this.RecommendBuyPrice-this.Average.FiveAverage) < this.conf.Unit {
			if this.UnBuyCount > 0 {
				log.Println("委托买单数", this.UnBuyCount)
				// 有正在挂的单
				return
			}
			if this.BuyPosition.Volume > 0 && this.BuyPosition.ProfitRate >= -1 {
				log.Println("当前买单浮盈率", this.BuyPosition.ProfitRate)
				// 每跌 1000% 50 倍 即 跌20% 进行补仓
				return
			}
			orderPriceType = "opponent"
			// 可以买入
			_, err := this.client.SwapOrder(symbol, symbol, orderPriceType, clientOrderID,
				this.RecommendBuyPrice, volume, direction, offset, lever_rate)
			if err != nil {
				log.Println("下单失败", err)
				return
			}
			log.Println("买单市价下单成功", this.RecommendBuyPrice, volume)
		}
		return
	}
	if this.RecommendDirection == 1 && this.SellPosition.Volume/this.conf.Volume >= 2 {
		return
	}
	if this.RecommendDirection == 0 && this.BuyPosition.Volume/this.conf.Volume >= 2 {
		return
	}
	if this.Average.FiveAverage < this.Average.TenAverage &&
		this.Average.TenAverage < this.Average.ThirtyAverage {
		log.Println("**********进行策略2分析", this.RecommendDirection, this.RecommendBuyPrice, this.RecommendSellPrice)
		if math.Abs(this.RecommendBuyPrice-this.Average.FiveAverage) < this.conf.Unit {
			if this.UnSellCount > 0 {
				log.Println("委托空单数", this.UnSellCount)
				// 有正在挂的单
				return
			}
			if this.SellPosition.Volume > 0 && this.SellPosition.ProfitRate >= -1 {
				log.Println("当前卖单浮盈率", this.SellPosition.ProfitRate)
				// 每跌 100% 50 倍 即 跌2% 进行补仓
				return
			}
			direction = "sell"
			orderPriceType = "opponent"
			// 可以买入
			_, err := this.client.SwapOrder(symbol, symbol, orderPriceType, clientOrderID,
				this.RecommendBuyPrice, volume, direction, offset, lever_rate)
			if err != nil {
				log.Println("下单失败", err)
				return
			}
			log.Println("卖单市价下单成功", this.RecommendBuyPrice, volume)
		}
		return
	}
	if this.RecommendDirection == 0 && this.MinAverage < this.RecommendBuyPrice {
		this.RecommendDirection = -1
		log.Println("**********进行策略3分析 [挂单]", this.RecommendDirection, this.RecommendBuyPrice, this.MinAverage)
		if this.UnBuyCount > 0 {
			// 有正在挂的单
			log.Println("委托买单数", this.UnBuyCount)
			return
		}
		// 推荐买
		if this.BuyPosition.Volume > 0 && this.BuyPosition.ProfitRate >= -1 {
			log.Println("当前买单浮盈率", this.BuyPosition.ProfitRate)
			// 每跌 100% 50 倍 即 跌2% 进行补仓
			return
		}
		_, err := this.client.SwapOrder(symbol, symbol, orderPriceType, clientOrderID,
			this.MinAverage, volume, direction, offset, lever_rate)
		if err != nil {
			log.Println("买单错误", err)
			return
		}
		log.Println("买单挂单成功", this.Average.TenAverage, volume)
		return
	}

	if this.RecommendDirection == 1 && this.MaxAverage > this.RecommendSellPrice {
		this.RecommendDirection = -1
		log.Println("**********进行策略4分析 [挂单]", this.RecommendDirection, this.RecommendBuyPrice, this.MaxAverage)
		if this.UnSellCount > 0 {
			// 有正在挂的单
			log.Println("委托买单数", this.UnSellCount)
			return
		}
		if this.SellPosition.Volume > 0 && this.SellPosition.ProfitRate >= -1 {
			log.Println("当前卖单浮盈率", this.SellPosition.ProfitRate)
			// 每跌 100% 50 倍 即 跌2% 进行补仓
			return
		}
		direction = "sell"
		// 推荐买
		or, err := this.client.SwapOrder(symbol, symbol, orderPriceType, clientOrderID,
			this.MaxAverage, volume, direction, offset, lever_rate)
		if err != nil {
			log.Println("卖单错误", err)
			return
		}
		log.Println("卖单挂单成功", or.Status, this.Average.TenAverage, volume, or.Data.OrderID, or.ErrMsg)
		return
	}
}
